package org.genuflex.command
{
	import com.adobe.cairngorm.commands.SequenceCommand;

	import mx.rpc.IResponder;
	import mx.rpc.events.ResultEvent;
	import mx.rpc.events.FaultEvent;
	import mx.collections.ArrayCollection;

	import org.genuflex.utils.GenuflexLogger;
	import org.genuflex.model.GenuflexModelLocator;
	import mx.utils.ObjectUtil;
	import org.genuflex.vo.GenuflexMetadata;
	import org.genuflex.vo.BelongsToAssociation;
	import org.genuflex.vo.HasManyAssociation;
	import org.genuflex.vo.HasAndBelongsToManyAssociation;
	import org.genuflex.utils.GenuflexDataFunctions;
	import flash.utils.getQualifiedClassName;

	public class GenuflexCommand extends SequenceCommand implements IResponder
	{
		
		[Bindable]
		public var genuflexModel : GenuflexModelLocator;
		
		public var objectClass : Class = Object;
		public var additionalResultFunction : Function;
		public var modelCollectionName : String;
		
		// mergeResults should usually be false, unless you are dealing with a large data set
		// that is brought back in pieces
		public var mergeResults : Boolean = false; 
		
		// skipHasAndBelongsToManyUpdate should usually be false, unless you are dealing with a large data set
		// that is brought back in pieces and you don't want a search to clear out existing data
		public var skipHasAndBelongsToManyUpdate : Boolean = false; 
		
		public function result( data : Object ) : void {   
			var event : ResultEvent = data as ResultEvent;
			
			//Destroy Commands have their own Result Handler
			if (getQualifiedClassName(this).indexOf("Destroy") >= 0) {
				handleDestroyResultData(event.result);
			}
			else {
				handleResultData(event.result);
			}
		}
		
		//This is a central point for handling data coming back from the server
		public function handleResultData(data:Object):void
		{					
			if (data != null) {
				if (data is Array) {
					
					//This needs some ATTENTION... right now, it is assuming that an array
					// coming back from the server constitutes ALL data for this particular VO
					// This should change to merge what is on the model and what is coming back from the server
					var resultArray : ArrayCollection = new ArrayCollection();
						
					//If we are to merge the results, we need to base the results array on what we already have
					if (mergeResults && GenuflexDataFunctions.isNotEmpty(genuflexModel[modelCollectionName])) {
						resultArray = new ArrayCollection(ArrayCollection(genuflexModel[modelCollectionName]).toArray());
					}
						
					//Update Data Cache
					for each(var object : Object in data) {
						var objTmp : Object = genuflexModel.dataCache.updateData(objectClass, object);
						
						//Don't add the same object twice
						if (!resultArray.contains(objTmp)) { 
							resultArray.addItem(objTmp);
						}
					}					
					
					//If an addditional result function has been specified, call it
					if (additionalResultFunction != null) {
						additionalResultFunction.call(this, resultArray);
					}
					
					//If a model collection name has been specified, add the data
					if (modelCollectionName != null) {
						genuflexModel[modelCollectionName] = resultArray;
					}

					//Update data across all associations
					for each(var belongsTo : BelongsToAssociation in genuflexModel.dataCache.belongsToAssociations(objectClass)) {
						genuflexModel.dataCache.updateBelongsToData(belongsTo.sourceClass, belongsTo.name, belongsTo.destClass, belongsTo.foreignKeyName);
					}
	
					for each(var hasMany : HasManyAssociation in genuflexModel.dataCache.hasManyAssociations(objectClass)) {
						genuflexModel.dataCache.updateHasManyData(hasMany.sourceClass, hasMany.name, hasMany.destClass, hasMany.foreignKeyName);
					}
	
					if (!skipHasAndBelongsToManyUpdate) {
						for each(var hasAndBelongsToMany : HasAndBelongsToManyAssociation in genuflexModel.dataCache.hasAndBelongsToManyAssociations(objectClass)) {
							genuflexModel.dataCache.updateHasAndBelongsToManyData(hasAndBelongsToMany.sourceClass, hasAndBelongsToMany.name, hasAndBelongsToMany.destClass, hasAndBelongsToMany.foreignKeyName);
						}
					}
								
				}
				else {
					//Don't push this single value into the cache if this class isn't being managed yet
					// it results in bad results down the road as the cache assumes it's looking at the entire
					// picture.
					var resultObject : Object = null;
					
					if (genuflexModel.dataCache.managedClasses.contains(objectClass)) {
						resultObject = (genuflexModel.dataCache.updateData(objectClass, data));
					}
					else {
						resultObject = data;
					}
					
					//Does this new object exist on the model?
					if (modelCollectionName != null && 
						genuflexModel[modelCollectionName] is ArrayCollection &&
						!(genuflexModel[modelCollectionName] as ArrayCollection).contains(resultObject)) {
						
						(genuflexModel[modelCollectionName] as ArrayCollection).addItem(resultObject);
					}
					
					if (additionalResultFunction != null) {
						additionalResultFunction.call(this, resultObject);
					}
				}
			}
		}		

		public function handleDestroyResultData(data : Object) : void {
			if (data != null) {
				//This should be an array of deleted IDs
				if (data is Array) {
					for each(var id:int in data) {
												
						var tmpObj : Object = GenuflexDataFunctions.findByID(genuflexModel[modelCollectionName], id);
										
						GenuflexDataFunctions.removeFromArray(genuflexModel[modelCollectionName], tmpObj);
						genuflexModel.dataCache.removeData(objectClass, tmpObj);
					}
				}
			}		
		}

		public function fault( info : Object ) : void {   
			var event : FaultEvent = info as FaultEvent;     
			GenuflexLogger.Error( "GenuflexCommand data could not be loaded!" + event.fault.message);
		}		
	}
}